package org.jget.view;

import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.KeyboardFocusManager;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.Locale;
import java.util.Set;
import java.util.Vector;
import java.util.concurrent.ExecutionException;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.KeyStroke;
import javax.swing.Timer;
import javax.swing.filechooser.FileFilter;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableRowSorter;

import java.awt.Dimension;
import javax.swing.JMenuBar;
import javax.swing.JMenu;
import javax.swing.JMenuItem;

import org.apache.commons.lang.ArrayUtils;
import org.jget.controller.QNapController;
import org.jget.controller.QNapController.GetConfigurationNodeVisitor;
import org.jget.controller.QNapController.NetworkShare;
import org.jget.controller.QNapController.TorrentDownloadState;
import org.jget.view.utils.FileUtils;
import org.jget.view.utils.SwingWorker;

public class Mainframe extends JFrame {

	private QNapController controller = new QNapController();

	private static final long serialVersionUID = 1L;

	private JPanel jContentPane = null;

	private JTabbedPane jTabbedPane = null;

	private JTable jTableRunning = null;
	private JTable jTablePaused = null;
	private JTable jTableFinished = null;

	private JScrollPane jScrollPaneRunning = null;
	private JScrollPane jScrollPanePaused = null;
	private JScrollPane jScrollPaneFinished = null;

	private JPanel jPanel = null;

	private JPanel jPanel1 = null;

	private JButton upJButton = null;

	private JButton downJButton = null;

	// Create a file chooser
	final JFileChooser jAddFileChooser = new JFileChooser();

	private JButton moveToJButton = null;

	private Timer timer;

	private JButton pauseJButton = null;

	private JButton resumeJButton = null;

	private JButton infoJButton = null;

	private JButton deleteJButton = null;

	private JButton deleteAllJButton = null;

	private JMenuBar jJMenuBar = null;

	private JMenu settingsJMenu = null;
	private JMenu aboutJMenu = null;

	private JMenuItem configurationJMenuItem = null;
	// private JMenuItem serverConfigurationJMenuItem = null;

	private JMenuItem shareTimeJMenuItem = null;

	private JMenuItem setServerJMenuItem = null;

	private JMenu taskJMenu = null;

	private JMenuItem addBtJMenuItem = null;

	private JMenuItem addFtpJMenuItem = null;

	Comparator<String> dateComparator = new Comparator<String>() {
		public int compare(String str1, String str2) {
			try {
				Date date1 = parseDate(str1);
				Date date2 = parseDate(str2);
				return date1.compareTo(date2);
			} catch (Exception e) {
				e.printStackTrace();
			}
			return 0;
		}

		private Date parseDate(String timestamp) throws Exception {
			/*
			 * * we specify Locale.US since months are in english
			 */
			SimpleDateFormat sdf = new SimpleDateFormat(
					"EEE MMM d HH:mm:ss yyyy", Locale.US);
			return sdf.parse(timestamp);
		}
	};

	Comparator<String> etaComparator = new Comparator<String>() {
		public int compare(String str1, String str2) {
			try {
				Integer etaSeconds1 = -1;
				int etaSeconds2 = -1;

				if (str1.isEmpty() == false) {
					etaSeconds1 = parseEta(str1);
				}
				if (str2.isEmpty() == false) {
					etaSeconds2 = parseEta(str2);
				}
				return etaSeconds1.compareTo(etaSeconds2);
			} catch (Exception e) {
				e.printStackTrace();
			}
			return 0;
		}

		private int parseEta(String timestamp) throws Exception {
			/*
			 * * we specify Locale.US since months are in english
			 */
			String[] times = timestamp.split(":");
			int seconds = Integer.parseInt(times[2])
					+ Integer.parseInt(times[1]) * 60
					+ Integer.parseInt(times[0]) * 3600;
			return seconds;
		}
	};

	/**
	 * This method initializes jPanel
	 * 
	 * @return javax.swing.JPanel
	 */
	private JPanel getBottomJPanel() {
		if (jPanel == null) {
			jPanel = new JPanel();
			jPanel.setLayout(new FlowLayout());
		}
		return jPanel;
	}

	/**
	 * This method initializes jPanel1
	 * 
	 * @return javax.swing.JPanel
	 */
	private JPanel getTopJPanel() {
		if (jPanel1 == null) {
			jPanel1 = new JPanel();
			jPanel1.setLayout(new FlowLayout());
			jPanel1.add(getUpJButton());
			jPanel1.add(getDownJButton(), null);
			jPanel1.add(getMoveToJButton(), null);
			jPanel1.add(getPauseJButton(), null);
			jPanel1.add(getResumeJButton(), null);
			jPanel1.add(getInfoJButton(), null);
			jPanel1.add(getDeleteJButton(), null);
			jPanel1.add(getDeleteAllJButton(), null);
		}
		return jPanel1;
	}

	/**
	 * This method initializes jButton2
	 * 
	 * @return javax.swing.JButton
	 */
	private JButton getUpJButton() {
		if (upJButton == null) {
			upJButton = new JButton();
			upJButton.setText("Up");
			upJButton
					.setToolTipText("<html>Moves the selected task(s) up by one position in the list.</html>");
			upJButton.addMouseListener(new java.awt.event.MouseAdapter() {
				public void mouseClicked(java.awt.event.MouseEvent e) {
					moveUpSelectedTasks();
					getRunningList();
				}
			});
		}
		return upJButton;
	}

	private void moveUpSelectedTasks() {
		int[] selectedTasks = getVisibleTable().getSelectedRows();
		for (int taskIndex : selectedTasks) {
			this.controller.moveTaskUp(getVisibleTable()
					.convertRowIndexToModel(taskIndex));
		}
	}

	private void moveDownSelectedTasks() {
		int[] selectedTasks = getJTableRunning().getSelectedRows();
		for (int taskIndex : selectedTasks) {
			this.controller.moveTaskDown(getVisibleTable()
					.convertRowIndexToModel(taskIndex));
		}
	}

	/**
	 * This method initializes jButton3
	 * 
	 * @return javax.swing.JButton
	 */
	private JButton getDownJButton() {
		if (downJButton == null) {
			downJButton = new JButton();
			downJButton.setText("Down");
			downJButton
					.setToolTipText("<html>Moves the selected task(s) down by one position in the list.</html>");
			downJButton.addMouseListener(new java.awt.event.MouseAdapter() {
				public void mouseClicked(java.awt.event.MouseEvent e) {
					moveDownSelectedTasks();
					getRunningList();
				}
			});
		}
		return downJButton;
	}

	/**
	 * This method initializes jButton4
	 * 
	 * @return javax.swing.JButton
	 */
	private JButton getMoveToJButton() {
		if (moveToJButton == null) {
			moveToJButton = new JButton();
			moveToJButton.setText("Move");
			moveToJButton.setMnemonic(KeyEvent.VK_M);
			moveToJButton
					.setToolTipText("<html>Moves the selected tasks to the specified position.<br />"
							+ "Take care, as this this moves the tasks consecutively by one position until they reach their target position. "
							+ "<br /><b>During this time, the application is not responding.</b></html>");
			moveToJButton.setActionCommand("Move");
			moveToJButton.addMouseListener(new java.awt.event.MouseAdapter() {
				public void mouseClicked(java.awt.event.MouseEvent e) {
					int[] selectedTasks = getJTableRunning().getSelectedRows();

					String selected = "";
					for (int selectedTask : selectedTasks) {
						Object[] possibilities = new Object[getJTableRunning()
								.getRowCount() - 1];
						int j = 0;
						for (int i = 0; i < getJTableRunning().getRowCount(); i++) {
							if (i != selectedTask) {
								possibilities[j] = i + 1;
								if (selected.equals("")) {
									selected = Integer.toString(i + 1);
								}
								j++;
							}
						}
						Object result = JOptionPane.showInputDialog(
								Mainframe.this, "New position:",
								"Customized Dialog", JOptionPane.PLAIN_MESSAGE,
								null, possibilities, selected);
						if (result != null) {
							Integer newPos = (Integer) result - 1;
							Mainframe.this.controller.moveTaskTo(
									getVisibleTable().convertRowIndexToModel(
											selectedTask), newPos.intValue());
							getRunningList();

						}
					}
				}
			});
		}
		return moveToJButton;
	}

	/**
	 * This method initializes pauseJButton
	 * 
	 * @return javax.swing.JButton
	 */
	private JButton getPauseJButton() {
		if (pauseJButton == null) {
			pauseJButton = new JButton();
			pauseJButton.setText("Pause");
			pauseJButton.setMnemonic(KeyEvent.VK_P);
			pauseJButton
					.setToolTipText("<html>Pauses downloading the selected task(s) and moves them into the paused downloads tab.</html>");
			pauseJButton.addMouseListener(new java.awt.event.MouseAdapter() {
				public void mouseClicked(java.awt.event.MouseEvent e) {

					int[] selectedTasks = getJTableRunning().getSelectedRows();
					for (int taskIndex : selectedTasks) {
						Mainframe.this.controller.pauseTask(getVisibleTable()
								.convertRowIndexToModel(taskIndex));
					}

				}
			});
		}
		return pauseJButton;
	}

	/**
	 * This method initializes restartJButton
	 * 
	 * @return javax.swing.JButton
	 */
	private JButton getResumeJButton() {
		if (resumeJButton == null) {
			resumeJButton = new JButton();
			resumeJButton.setText("Resume");
			resumeJButton.setMnemonic(KeyEvent.VK_A);
			resumeJButton
					.setToolTipText("<html>Resumes downloading the selected paused download task(s).</html>");
			resumeJButton.addMouseListener(new java.awt.event.MouseAdapter() {
				public void mouseClicked(java.awt.event.MouseEvent e) {
					int[] selectedTasks = getVisibleTable().getSelectedRows();
					QNapController.TorrentDownloadState state = getActiveState();
					for (int taskIndex : selectedTasks) {
						Mainframe.this.controller.restartTask(getVisibleTable()
								.convertRowIndexToModel(taskIndex), state);
					}
				}
			});
		}
		return resumeJButton;
	}

	private TorrentDownloadState getActiveState() {
		int selectedTab = getJTabbedPane().getSelectedIndex();
		switch (selectedTab) {
		case 0:
			return TorrentDownloadState.Running;
		case 1:
			return TorrentDownloadState.Paused;
		case 2:
			return TorrentDownloadState.Finished;
		default:
			return null;
		}
	}

	/**
	 * This method initializes infoJButton
	 * 
	 * @return javax.swing.JButton
	 */
	private JButton getInfoJButton() {
		if (infoJButton == null) {
			infoJButton = new JButton();
			infoJButton.setText("Info");
			infoJButton.setMnemonic(KeyEvent.VK_I);
			infoJButton
					.setToolTipText("<html>Shows diagnostic information about the selected task. May be used to see why an active task is not downloading anything.</html>");
			infoJButton.addMouseListener(new java.awt.event.MouseAdapter() {
				public void mouseClicked(java.awt.event.MouseEvent e) {
					QNapController.TorrentDownloadState state = getActiveState();
					int[] selectedTasks = getVisibleTable().getSelectedRows();
					for (int taskIndex : selectedTasks) {
						String info = Mainframe.this.controller
								.getTaskDownloadInfo(taskIndex, state);

						JOptionPane.showMessageDialog(Mainframe.this, info);
					}
				}
			});
		}
		return infoJButton;
	}

	/**
	 * This method initializes deleteJButton
	 * 
	 * @return javax.swing.JButton
	 */
	private JButton getDeleteJButton() {
		if (deleteJButton == null) {
			deleteJButton = new JButton();
			deleteJButton.setText("Delete");
			deleteJButton.setMnemonic(KeyEvent.VK_D);
			deleteJButton
					.setToolTipText("<html>Removes a task from the list. You can choose to also delete all files that have been downloaded already.</html>");
			deleteJButton.addMouseListener(new java.awt.event.MouseAdapter() {
				public void mouseClicked(java.awt.event.MouseEvent e) {
					int[] selectedTasks = getVisibleTable().getSelectedRows();
					QNapController.TorrentDownloadState page = getActiveState();
					for (int taskIndex : selectedTasks) {
						Object[] options = { "Keep files", "Delete files",
								"Cancel" };
						int option = JOptionPane
								.showOptionDialog(
										Mainframe.this,
										"Do you want to keep the files already downloaded?",
										"Remove download task",
										JOptionPane.YES_NO_CANCEL_OPTION,
										JOptionPane.QUESTION_MESSAGE, null,
										options, options[0]);

						if (option == 0) {
							Mainframe.this.controller.deleteTask(
									getVisibleTable().convertRowIndexToModel(
											taskIndex), page, false);
						} else if (option == 1) {
							Mainframe.this.controller.deleteTask(
									getVisibleTable().convertRowIndexToModel(
											taskIndex), page, true);
						}
					}
				}
			});
		}
		return deleteJButton;
	}

	/**
	 * This method initializes deleteAllJButton
	 * 
	 * @return javax.swing.JButton
	 */
	private JButton getDeleteAllJButton() {
		if (deleteAllJButton == null) {
			deleteAllJButton = new JButton();
			deleteAllJButton.setText("Delete (all)");
			deleteAllJButton.setMnemonic(KeyEvent.VK_A);
			deleteAllJButton
					.setToolTipText("<html>Removes all selected tasks from the list. You can choose to also delete all files that have been downloaded already.</html>");
			deleteAllJButton
					.addMouseListener(new java.awt.event.MouseAdapter() {
						public void mouseClicked(java.awt.event.MouseEvent e) {
							int[] selectedTasks = getVisibleTable()
									.getSelectedRows();
							QNapController.TorrentDownloadState page = getActiveState();

							Object[] options = { "Keep files", "Delete files",
									"Cancel" };
							int option = JOptionPane
									.showOptionDialog(
											Mainframe.this,
											"Do you want to keep the files already downloaded?\n"
													+ "(This will be applied to all selected downloads).",
											"Remove download tasks",
											JOptionPane.YES_NO_CANCEL_OPTION,
											JOptionPane.QUESTION_MESSAGE, null,
											options, options[0]);
							// iterate through the list in reverse order to
							// prevent the service from removing items not
							// selected, due to changing id's (issue=22)
							Arrays.sort(selectedTasks);
							for (int i = selectedTasks.length - 1; i >= 0; i--) {
								if (option == 0) {
									Mainframe.this.controller.deleteTask(
											getVisibleTable()
													.convertRowIndexToModel(
															selectedTasks[i]),
											page, false);
								} else if (option == 1) {
									Mainframe.this.controller.deleteTask(
											getVisibleTable()
													.convertRowIndexToModel(
															selectedTasks[i]),
											page, true);
								}
							}
						}
					});
		}
		return deleteAllJButton;
	}

	/**
	 * This method initializes jJMenuBar
	 * 
	 * @return javax.swing.JMenuBar
	 */
	private JMenuBar getJJMenuBar() {
		if (jJMenuBar == null) {
			jJMenuBar = new JMenuBar();
			jJMenuBar.setPreferredSize(new Dimension(0, 20));
			jJMenuBar.add(getTaskJMenu());
			jJMenuBar.add(getSettingsJMenu());
			jJMenuBar.add(getAboutJMenu());
		}
		return jJMenuBar;
	}

	/**
	 * This method initializes settingsJMenu
	 * 
	 * @return javax.swing.JMenu
	 */
	private JMenu getSettingsJMenu() {
		if (settingsJMenu == null) {
			settingsJMenu = new JMenu();
			settingsJMenu.setText("Settings");
			settingsJMenu.add(getConfigurationJMenuItem());
			settingsJMenu.add(getSetServerJMenuItem());
			settingsJMenu.add(getShareTimeJMenuItem());
		}
		return settingsJMenu;
	}

	/**
	 * This method initializes settingsJMenu
	 * 
	 * @return javax.swing.JMenu
	 */
	private JMenu getAboutJMenu() {
		if (aboutJMenu == null) {
			aboutJMenu = new JMenu();
			aboutJMenu.setText("About");
			aboutJMenu.addMouseListener(new java.awt.event.MouseAdapter() {
				public void mousePressed(java.awt.event.MouseEvent e) {
					JOptionPane
							.showMessageDialog(Mainframe.this,
									"JGet v0.3\nCopyright (C) 2008 - 2010\n\nNicolas Lefebvre\n\nThijs Brobbel");
				}
			});
		}
		return aboutJMenu;
	}

	// private JMenuItem getServerConfigurationJMenuItem() {
	// if (serverConfigurationJMenuItem == null) {
	// serverConfigurationJMenuItem = new JMenuItem();
	// serverConfigurationJMenuItem.setText("Server configuration");
	// serverConfigurationJMenuItem.addMouseListener(new
	// java.awt.event.MouseAdapter() {
	// public void mousePressed(java.awt.event.MouseEvent e) {
	// showDownloadServerDialog();
	// }
	// });
	// }
	// return configurationJMenuItem;
	// }

	/**
	 * This method initializes configurationJMenuItem
	 * 
	 * @return javax.swing.JMenuItem
	 */
	private JMenuItem getConfigurationJMenuItem() {
		if (configurationJMenuItem == null) {
			configurationJMenuItem = new JMenuItem();
			configurationJMenuItem.setText("Configuration");
			configurationJMenuItem
					.addMouseListener(new java.awt.event.MouseAdapter() {
						public void mousePressed(java.awt.event.MouseEvent e) {
							GetConfigurationNodeVisitor visitor = Mainframe.this.controller
									.getConfiguration();
							ConfigurationDialog dialog = new ConfigurationDialog(
									Mainframe.this);
							dialog.setUpRate(visitor.getMaxUploadRate());
							dialog.setDownRate(visitor.getMaxDownloadRate());
							dialog.setNatPortForwarding(visitor
									.isNatPortForwarding());
							dialog.setMaxDownloads(visitor
									.getMaxConcurrentDownloads());
							dialog.setPortRangeStart(visitor
									.getBtPortRangeStart());
							dialog.setPortRangeEnd(visitor.getBtPortStartEnd());

							dialog.setVisible(true);

							if (dialog.isOk()) {
								Mainframe.this.controller.setConfiguration(
										dialog.getMaxDownloads(),
										dialog.getDownRate(),
										dialog.getUpRate(),
										dialog.getPortRangeStart(),
										dialog.getPortRangeEnd(),
										dialog.isNatPortForwarding());
							}
						}
					});
		}
		return configurationJMenuItem;
	}

	/**
	 * This method initializes shareTimeJMenuItem
	 * 
	 * @return javax.swing.JMenuItem
	 */
	private JMenuItem getShareTimeJMenuItem() {
		if (shareTimeJMenuItem == null) {
			shareTimeJMenuItem = new JMenuItem();
			shareTimeJMenuItem.setText("Set task's seeding time");
			shareTimeJMenuItem
					.addMouseListener(new java.awt.event.MouseAdapter() {
						public void mousePressed(java.awt.event.MouseEvent e) {
							int[] selectedTasks = getJTableRunning()
									.getSelectedRows();
							QNapController.TorrentDownloadState page = getActiveState();
							for (int taskIndex : selectedTasks) {
								String result = (String) JOptionPane
										.showInputDialog(
												Mainframe.this,
												"Seeding time after completion of download task:",
												"Edit seeding time",
												JOptionPane.PLAIN_MESSAGE,
												null,
												null,
												Mainframe.this.controller
														.getShareTime(
																getVisibleTable()
																		.convertRowIndexToModel(
																				taskIndex),
																page));

								// If a string was returned, say so.
								if ((result != null) && (result.length() > 0)) {
									try {
										Mainframe.this.controller
												.setShareTime(
														getVisibleTable()
																.convertRowIndexToModel(
																		taskIndex),
														page,
														Integer.parseInt(result));
									} catch (NumberFormatException nfe) {
										nfe.printStackTrace();
									}
									return;
								}
							}
						}
					});
		}
		return shareTimeJMenuItem;
	}

	/**
	 * This method initializes taskJMenu
	 * 
	 * @return javax.swing.JMenu
	 */
	private JMenu getTaskJMenu() {
		if (taskJMenu == null) {
			taskJMenu = new JMenu();
			taskJMenu.setText("Task");
			taskJMenu.add(getAddBtJMenuItem());
			taskJMenu.add(getAddFtpJMenuItem());
		}
		return taskJMenu;
	}

	/**
	 * This method initializes addBtJMenuItem
	 * 
	 * @return javax.swing.JMenuItem
	 */
	private JMenuItem getAddBtJMenuItem() {
		if (addBtJMenuItem == null) {
			addBtJMenuItem = new JMenuItem();
			addBtJMenuItem.setText("Add torrent ...");
			addBtJMenuItem.addMouseListener(new java.awt.event.MouseAdapter() {
				public void mousePressed(java.awt.event.MouseEvent e) {
					jAddFileChooser
							.setFileSelectionMode(JFileChooser.FILES_ONLY);
					jAddFileChooser
							.addChoosableFileFilter(new TorrentFileFilter());
					jAddFileChooser.setAcceptAllFileFilterUsed(false);
					int returnVal = jAddFileChooser
							.showOpenDialog(Mainframe.this);
					if (returnVal == JFileChooser.APPROVE_OPTION) {
						File file = jAddFileChooser.getSelectedFile();
						// Add the torrent to the server
						Mainframe.this.controller.addTorrent(file);
						Mainframe.this.controller
								.getList(TorrentDownloadState.Running);
					}
				}
			});
		}
		return addBtJMenuItem;
	}

	/**
	 * This method initializes addBtJMenuItem
	 * 
	 * @return javax.swing.JMenuItem
	 */
	private JMenuItem getSetServerJMenuItem() {
		if (setServerJMenuItem == null) {
			setServerJMenuItem = new JMenuItem();
			setServerJMenuItem.setText("Server configuration");
			setServerJMenuItem
					.addMouseListener(new java.awt.event.MouseAdapter() {
						public void mousePressed(java.awt.event.MouseEvent e) {
							Mainframe.this.showDownloadServerDialog();
						}

					});
		}
		return setServerJMenuItem;
	}

	private boolean showDownloadServerDialog() {
		DownloadServerDialog dialog = new DownloadServerDialog(Mainframe.this,
				Mainframe.this.controller);

		dialog.setVisible(true);

		if (dialog.isOk()) {
			this.controller.setServer(dialog.getServerAddress(),
					dialog.getUsername(), dialog.getPassword(),
					dialog.getPort());
			if (dialog.doRememberCredentials()) {
				this.controller.saveCredentials();
			}
		}
		return dialog.isOk();
	}

	/**
	 * This method initializes addFtpJMenuItem
	 * 
	 * @return javax.swing.JMenuItem
	 */
	private JMenuItem getAddFtpJMenuItem() {
		if (addFtpJMenuItem == null) {
			addFtpJMenuItem = new JMenuItem();
			addFtpJMenuItem.setText("Add FTP/HTTP ...");
			addFtpJMenuItem.addMouseListener(new java.awt.event.MouseAdapter() {
				public void mousePressed(java.awt.event.MouseEvent e) {
					showAddFtpDialog(null);
				}
			});
		}
		return addFtpJMenuItem;
	}

	private void showAddFtpDialog(final String url) {
		Vector<NetworkShare> networkShares = Mainframe.this.controller
				.getNetworkShares();
		AddFtpDialog dialog = new AddFtpDialog(Mainframe.this, networkShares,
				url);

		dialog.setVisible(true);

		if (dialog.isOk()) {
			Mainframe.this.controller.addFtp(dialog.getUrl(),
					dialog.getSaveFolder(), dialog.useCredentials(),
					dialog.getUsername(), dialog.getPassword());
		}
	}

	public static void main(String[] args) {

		// LOG: enable this to trace the discovery of the logging property file
		// 'commons-logging.properties'
		//System.setProperty("org.apache.commons.logging.diagnostics.dest",	"STDOUT");
		// LOG: enable this to test logging easily
//		QNapController.LOG.info("Hoi");
//		QNapController.LOG.debug("Dag");
//		System.exit(0);

		// Ask for window decorations provided by the look and feel.
		// JFrame.setDefaultLookAndFeelDecorated(true);

		URL url2 = null;
		try {
			url2 = new URL(
					"ftp://username:password@ftp.whatever.com/file.zip;type=i");

		} catch (MalformedURLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		url2.getUserInfo();

		// 1. Create the frame.
		Mainframe mainFrame = new Mainframe("JGet");

		// 2. Optional: What happens when the frame closes?
		mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		// 4. Size the frame.
		mainFrame.pack();

		// 5. Show it.
		mainFrame.setVisible(true);

		// Check the arguments
		if (args.length > 0) {
			boolean exitAfterTaskAdd = false;

			for (int i = 0; i < args.length; i++) {
				// Object result = JOptionPane.showInputDialog("Args " + i +
				// ": " + args[i]
				// + " ext: "
				// + getFileExtension(args[i]));

				if (args[i].equalsIgnoreCase("?")
						|| args[i].equalsIgnoreCase("-h")
						|| args[i].equalsIgnoreCase("--help")
						|| args[i].equalsIgnoreCase("h")
						|| args[i].equalsIgnoreCase("help")) {
					System.out.println("Arguments:");
					System.out
							.println("argument starting with ftp\tStart downloading the specified ftp url");
					System.out
							.println("argument starting with http\tStart downloading the specified http url");
					System.out
							.println("argument ending with .torrent\tStart downloading the specified torrent");
					System.out
							.println("exit\tExit application after having started the download tasks specified in the command line");
					System.out
							.println("? or -h or --help or h or help\tShow this message");
				} else if (args[i].equalsIgnoreCase("exit")) {
					exitAfterTaskAdd = true;
				} else if (args[i].startsWith("http")
						|| args[i].startsWith("ftp")) {
					mainFrame.showAddFtpDialog(args[i]);
				} else if (getFileExtension(args[i])
						.equalsIgnoreCase("torrent")) {
					File file = new File(args[i]);
					if (file.exists()) {
						mainFrame.controller.addTorrent(file);
					}
				}
			}

			if (exitAfterTaskAdd) {
				System.exit(0);
			}
		}
	}

	private static String getFileExtension(String fileName) {
		// getting the extension of a filename, (plain or including dirname)
		// This code is much faster than any regex technique.

		// extension without the dot
		String ext;

		// where the last dot is. There may be more than one.
		int dotPlace = fileName.lastIndexOf('.');

		if (dotPlace >= 0) {
			fileName.substring(0, dotPlace);

			// possibly empty
			ext = fileName.substring(dotPlace + 1);
		} else {
			ext = "";
		}

		return ext;
	}

	/**
	 * @param owner
	 */
	public Mainframe(String title) {
		super(title);
		if (this.controller.checkServer() == false
				&& showDownloadServerDialog() == false) {
			System.exit(0);
		}
		initialize();
	}

	/**
	 * This method initializes this
	 * 
	 * @return void
	 */
	private void initialize() {
		this.setSize(637, 214);
		this.setJMenuBar(getJJMenuBar());
		this.setContentPane(getJContentPane());

		int FIVE_SECONDS = 5000;
		// Create a timer.
		timer = new Timer(FIVE_SECONDS, new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				@SuppressWarnings("rawtypes")
				SwingWorker worker = new SwingWorker() {

					@Override
					public Vector<String[]> construct() {
						Vector<String[]> tasks = Mainframe.this
								.getVisibleTaskList();
						return tasks;
					}

					@SuppressWarnings("unchecked")
					@Override
					public void finished() {
						try {
							Mainframe.this.updateTable(getVisibleTable(),
									(Vector<String[]>) get());
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						} catch (ExecutionException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}
				};
				worker.start();
			}
		});

		updateControls();

		// Start the timer
		timer.start();
	}

	/**
	 * This method initializes jContentPane
	 * 
	 * @return javax.swing.JPanel
	 */
	private JPanel getJContentPane() {
		if (jContentPane == null) {
			jContentPane = new JPanel();
			jContentPane.setLayout(new BorderLayout());
			jContentPane.add(getTopJPanel(), BorderLayout.PAGE_START);
			jContentPane.add(getJTabbedPane(), BorderLayout.CENTER);
			jContentPane.add(getBottomJPanel(), BorderLayout.PAGE_END);

			// set the ctrl-tab key to navigate through the tabs (issue=21)
			final int FORWARD = KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS;

			Action nextTabAction = new AbstractAction("nextTab") {
				public void actionPerformed(ActionEvent evt) {
					forwardTab();
				}

				private static final long serialVersionUID = -1124827786403794503L;
			};

			// remove the mapping of ctrl-tab to forward traversal
			Set<KeyStroke> tabSet = Collections.singleton(KeyStroke
					.getKeyStroke(KeyEvent.VK_TAB, 0));
			jContentPane.setFocusTraversalKeys(FORWARD, tabSet);
			InputMap map = jContentPane
					.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);

			// add the mapping
			KeyStroke ctrlTab = KeyStroke.getKeyStroke(KeyEvent.VK_TAB,
					InputEvent.CTRL_DOWN_MASK);
			map.put(ctrlTab, nextTabAction.getValue(Action.NAME));
			jContentPane.getActionMap().put(
					nextTabAction.getValue(Action.NAME), nextTabAction);
		}
		return jContentPane;
	}

	/**
	 * This method initializes jTabbedPane
	 * 
	 * @return javax.swing.JTabbedPane
	 */
	private JTabbedPane getJTabbedPane() {
		if (jTabbedPane == null) {
			jTabbedPane = new JTabbedPane();
			jTabbedPane.setName("jTabbedPane");
			jTabbedPane.addTab("Active downloads", null,
					getJScrollPaneRunning(), null);
			jTabbedPane.addTab("Paused downloads", null,
					getJScrollPanePaused(), null);
			jTabbedPane.addTab("Finished downloads", null,
					getJScrollPaneFinished(), null);
			// jTabbedPane.addTab(null, null, getTopJPanel(), null);
			jTabbedPane.addMouseListener(new MouseAdapter() {
				public void mouseClicked(MouseEvent e) {
					updateControls();
					updateVisibleTable();
				}
			});

			updateVisibleTable();
		}
		return jTabbedPane;
	}

	public void forwardTab() {
		switchTab(true);
	}

	public void backwardTab() {
		switchTab(false);
	}

	private void switchTab(boolean forward) {
		int nextIndex = forward ? +1 : -1;
		JTabbedPane thePane = getJTabbedPane();
		int tabCount = thePane.getTabCount();
		thePane.setSelectedIndex(
		// Java's modulo implementation won't return a positive remainder, so
		// the index is
		// being increased by the tab count
		(tabCount + thePane.getSelectedIndex() + nextIndex) % tabCount);
	}

	private void updateControls() {
		int selectedTab = getJTabbedPane().getSelectedIndex();

		getShareTimeJMenuItem().setVisible(selectedTab == 0);
		getUpJButton().setVisible(selectedTab == 0);
		getDownJButton().setVisible(selectedTab == 0);
		getMoveToJButton().setVisible(selectedTab == 0);
		getPauseJButton().setVisible(selectedTab == 0);
		getResumeJButton().setVisible(selectedTab == 1);
	}

	private Vector<String[]> getVisibleTaskList() {
		int index = getJTabbedPane().getSelectedIndex();

		Vector<String[]> tasks = null;
		switch (index) {
		case 0:
			tasks = this.controller.getList(TorrentDownloadState.Running);
			break;
		case 1:
			tasks = this.controller.getList(TorrentDownloadState.Paused);
			break;
		case 2:
			tasks = this.controller.getList(TorrentDownloadState.Finished);
			break;
		}

		return tasks;
	}

	private JTable getVisibleTable() {
		int index = getJTabbedPane().getSelectedIndex();

		JTable table = null;
		switch (index) {
		case 0:
			table = getJTableRunning();
			break;
		case 1:
			table = getJTablePaused();
			break;
		case 2:
			table = getJTableFinished();
			break;
		}

		return table;
	}

	private void updateVisibleTable() {
		updateTable(getVisibleTable(), getVisibleTaskList());
	}

	private void updateTable(JTable table, Vector<String[]> tasks) {
		int rowCount = tasks.size();

		DefaultTableModel model = (DefaultTableModel) table.getModel();

		while (model.getRowCount() > rowCount) {
			model.removeRow(tasks.size());
		}
		for (int i = 0; i < rowCount; i++) {
			try {
				if (i < table.getRowCount()) {
					model.setValueAt(i + 1, i, 0);
					if (i < tasks.size()) {
						for (int j = 0; j < tasks.elementAt(i).length
								&& j < model.getColumnCount(); j++) {
							model.setValueAt(tasks.elementAt(i)[j], i, j + 1);
						}
					} else {
						for (int j = 1; j < table.getColumnCount(); j++) {
							model.setValueAt("", i, j);
						}
					}
				} else {

					String[] tmp = (String[]) ArrayUtils.add(
							tasks.elementAt(i), 0, Integer.toString(i + 1));
					model.addRow(tmp);
				}
			} catch (Exception e) {
			}
		}
	}

	private void getRunningList() {
		Vector<String[]> tasks = this.controller
				.getList(TorrentDownloadState.Running);
		updateTable(getJTableRunning(), tasks);
	}

	/**
	 * This method initializes jList1
	 * 
	 * @return javax.swing.JList
	 */
	private JTable getJTableRunning() {
		if (jTableRunning == null) {
			String[] header = { "Pos", "Download task", "Status", "Size (Mb)",
					"Done (%)", "Download rate (kb/s)", "Seeders", "Peers",
					"ETA (hours)" };
			DefaultTableModel model = new DefaultTableModel(header,
					header.length) {
				private static final long serialVersionUID = 1L;

				@SuppressWarnings({ "rawtypes", "unchecked" })
				public Class getColumnClass(int columnIndex) {
					switch (columnIndex) {
					case 0:
					case 6:
					case 7:
						return Integer.class;
					case 3:
					case 4:
					case 5:
						return Integer.class;

					default:
						return super.getColumnClass(columnIndex);
					}
				}
			};

			jTableRunning = new JTable(model);

			// Der TableRowSorter wird die Daten des Models sortieren
			TableRowSorter<DefaultTableModel> sorter = new TableRowSorter<DefaultTableModel>(
					model) {
				public Comparator<?> getComparator(int column) {
					if (column == 8) {
						return etaComparator;
					}
					return super.getComparator(column);
				}
			};

			// Der TableRowSorter wird die Daten des Models sortieren
			// TableRowSorter<DefaultTableModel> sorter
			// = new TableRowSorter<DefaultTableModel>(model);
			// Der Sorter muss dem JTable bekannt sein
			jTableRunning.setRowSorter(sorter);

			// Disable auto resizing to make the table horizontal scrollable
			jTableRunning.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
		}
		return jTableRunning;
	}

	private JTable getJTablePaused() {
		if (jTablePaused == null) {
			String[] header = { "Pos", "Download task", "Size (Mb)",
					"Done (%)", "Downloaded (Mb)", "Uploaded (Mb)",
					"Sharing duration", "Start time" };
			DefaultTableModel model = new DefaultTableModel(header, 20) {
				private static final long serialVersionUID = 1L;

				@SuppressWarnings({ "rawtypes", "unchecked" })
				public Class getColumnClass(int columnIndex) {
					switch (columnIndex) {
					case 0:
						return Integer.class;
					case 2:
					case 3:
					case 4:
					case 5:
						return Integer.class;

					default:
						return super.getColumnClass(columnIndex);
					}
				}
			};
			jTablePaused = new JTable(model);

			// Der TableRowSorter wird die Daten des Models sortieren
			TableRowSorter<DefaultTableModel> sorter = new TableRowSorter<DefaultTableModel>(
					model) {
				public Comparator<?> getComparator(int column) {
					if (column == 7) {
						return dateComparator;
					}
					return super.getComparator(column);
				}
			};
			// Der Sorter muss dem JTable bekannt sein
			jTablePaused.setRowSorter(sorter);

			// Disable auto resizing to make the table horizontal scrollable
			jTablePaused.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
		}
		return jTablePaused;
	}

	private JTable getJTableFinished() {
		if (jTableFinished == null) {
			String[] header = { "Pos", "Download task", "Size (Mb)",
					"Time finished", "Downloaded (Mb)", "Uploaded (Mb)",
					"Sharing duration", "Start time" };
			DefaultTableModel model = new DefaultTableModel(header, 20) {
				private static final long serialVersionUID = 1L;

				@SuppressWarnings({ "rawtypes", "unchecked" })
				public Class getColumnClass(int columnIndex) {
					switch (columnIndex) {
					case 0:
						return Integer.class;
					case 2:
					case 4:
					case 5:
						return Integer.class;

					default:
						return super.getColumnClass(columnIndex);
					}
				}
			};
			jTableFinished = new JTable(model);

			// Der TableRowSorter wird die Daten des Models sortieren
			TableRowSorter<DefaultTableModel> sorter = new TableRowSorter<DefaultTableModel>(
					model) {
				public Comparator<?> getComparator(int column) {
					if (column == 3 || column == 7) {
						return dateComparator;
					}
					return super.getComparator(column);
				}
			};
			// Der Sorter muss dem JTable bekannt sein
			jTableFinished.setRowSorter(sorter);

			// Disable auto resizing to make the table horizontal scrollable
			jTableFinished.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
		}
		return jTableFinished;
	}

	private JScrollPane getJScrollPanePaused() {

		if (jScrollPanePaused == null) {
			jScrollPanePaused = new JScrollPane(getJTablePaused());
		}

		return jScrollPanePaused;
	}

	private JScrollPane getJScrollPaneFinished() {

		if (jScrollPaneFinished == null) {
			jScrollPaneFinished = new JScrollPane(getJTableFinished());
		}

		return jScrollPaneFinished;
	}

	private JScrollPane getJScrollPaneRunning() {
		if (jScrollPaneRunning == null) {
			jScrollPaneRunning = new JScrollPane(getJTableRunning());
		}

		return jScrollPaneRunning;
	}

	class TorrentFileFilter extends FileFilter {
		@Override
		public boolean accept(File f) {
			if (f.isDirectory()) {
				return true;
			}
			String ext = FileUtils.getExtension(f);
			return ext != null && ext.equals("torrent");
		}

		@Override
		public String getDescription() {
			return "Only torrents";
		}
	}

} // @jve:decl-index=0:visual-constraint="10,10"
